package logic

import (
	"core/component/logger"
	"core/component/timer"
	"sync/atomic"
	"time"
)

const (
	ExtraPool             = 0
	PlayerToExtraPoolTime = 30
	OnePlayerMaxMatchTime = 30
	TwoPlayerMaxMatchTime = 20
	PvpRoomMaxPeople      = 3
)

type PvpPool struct {
	pools       map[int32][]*Player
	poolMapKey  int32
	Timer       *timer.Timer
	inPlayers   chan []*Player
	outPlayer   chan *Player
	extraPlayer chan []*Player
}

func newPvpPool() *PvpPool {
	pp := &PvpPool{
		pools:       make(map[int32][]*Player),
		inPlayers:   make(chan []*Player, 16*4096),
		outPlayer:   make(chan *Player, 16*4096),
		extraPlayer: make(chan []*Player, 16*4096),
		Timer:       timer.NewTimer(),
	}
	pp.openExtraPool()
	pp.openPool()
	pp.registerTimerHandler()
	go pp.loop()
	return pp
}

func (pp *PvpPool) loop() {
	ticker := time.NewTicker(500 * time.Millisecond)
	for {
		select {
		case <-ticker.C:
			pp.Timer.Loop()
		case players := <-pp.inPlayers:
			pp.addPlayers(players...)
		case player := <-pp.outPlayer:
			pp.delPvpPlayers(player)
		case player := <-pp.extraPlayer:
			pp.enterExtraPool(player)
		}
	}
}

func (pp *PvpPool) checkPvpPlayerCanJoinExtraPool() {
	ps := make([]*Player, 0)
	now := time.Now().Unix()
	for k, players := range pp.pools {
		if k == ExtraPool {
			continue
		}
		for _, p := range players {
			if now-p.joinPvpPoolTime >= PlayerToExtraPoolTime && len(players) < PvpRoomMaxPeople {
				ps = append(ps, p)
			}
		}
	}

	if len(ps) != 0 {
		for _, pl := range ps {
			pp.delPvpPlayers(pl)
		}
		pp.extraPlayer <- ps
	}
}

func (pp *PvpPool) pvpMatchPlayer() {
	for k, _ := range pp.pools {
		if k == ExtraPool {
			continue
		}
		for len(pp.pools[k]) >= PvpRoomMaxPeople {
			pp.playerMatchSuccess(pp.pools[k][:PvpRoomMaxPeople])
			pp.pools[k] = pp.pools[k][PvpRoomMaxPeople:]
		}
	}

	l := len(pp.pools[ExtraPool])
	switch l {
	case 1:
		if time.Now().Unix()-pp.pools[ExtraPool][0].joinPvpPoolTime >= OnePlayerMaxMatchTime {
			pp.playerMatchSuccess(pp.pools[ExtraPool][:l])
			pp.pools[ExtraPool] = pp.pools[ExtraPool][l:]
		}
	case 2:
		if time.Now().Unix()-pp.pools[ExtraPool][0].joinPvpPoolTime >= TwoPlayerMaxMatchTime {
			pp.playerMatchSuccess(pp.pools[ExtraPool][:l])
			pp.pools[ExtraPool] = pp.pools[ExtraPool][l:]
		}
	case 3:
		pp.playerMatchSuccess(pp.pools[ExtraPool][:l])
		pp.pools[ExtraPool] = pp.pools[ExtraPool][l:]
	}
}

// 玩家pvp成功
func (pp *PvpPool) playerMatchSuccess(players []*Player) {
	WorldGetMe().roomSrv.sendCreateRoomWithPlayers(players)
}

func (pp *PvpPool) addPlayerChannel(players ...*Player) {
	pp.inPlayers <- players
}

func (pp *PvpPool) addPlayers(players ...*Player) {
	poolKey := int32(1)
	for i := 1; i <= int(pp.poolMapKey); i++ {
		if len(pp.pools[pp.poolMapKey]) < 1000 {
			poolKey = pp.poolMapKey
			break
		}
		if i == int(pp.poolMapKey) {
			pp.openPool()
			poolKey = pp.poolMapKey
			break
		}
	}
	pp.pools[poolKey] = append(pp.pools[poolKey], players...)
	pp.setPlayerPoolKey(poolKey, players...)
}

func (pp *PvpPool) delPvpPlayers(player *Player) {
	pool, ok := pp.pools[player.poolMapKey]
	if !ok {
		return
	}
	for k, p := range pool {
		if p.playerId == player.playerId {
			pp.pools[player.poolMapKey] = append(pp.pools[player.poolMapKey][:k], pp.pools[player.poolMapKey][k+1:]...)
			logger.Infof("[deletePlayer] playerId=%v poolMapKey=%v,", player.playerId, player.poolMapKey)
			break
		}
	}
}

func (pp *PvpPool) setPlayerPoolKey(poolMapKey int32, players ...*Player) {
	now := time.Now().Unix()
	for _, player := range players {
		player.poolMapKey = poolMapKey
		player.joinPvpPoolTime = now
		logger.Infof("[setPlayerPoolKey] playerId=%v poolMapKey=%v,", player.playerId, player.poolMapKey)
	}
}

func (pp *PvpPool) removePvpPool(player *Player) {
	pp.outPlayer <- player
}

// 开启匹配池
func (pp *PvpPool) openPool() {
	atomic.AddInt32(&pp.poolMapKey, 1)
	pp.pools[pp.poolMapKey] = []*Player{}
	return
}

// 开启额外匹配池
func (pp *PvpPool) openExtraPool() {
	pp.pools[ExtraPool] = make([]*Player, 0)
}

func (pp *PvpPool) enterExtraPool(players []*Player) {
	for _, player := range players {
		player.joinPvpPoolTime = time.Now().Unix()
		player.poolMapKey = ExtraPool
		logger.Infof("[玩家进入extra匹配池] playerId %v poolMapKey %v", player.playerId, player.poolMapKey)
	}
	pp.pools[ExtraPool] = append(pp.pools[ExtraPool], players...)
}
